﻿using ArielCore.MixedDataBase.MySql;
using FluentAssertions;
using Microsoft.EntityFrameworkCore;
using NewRetail.Application.Components.Goods.Entity;
using NewRetail.Application.Components.Platform.Entity;
using NewRetail.Application.Components.Warehouse.Entity;
using NewRetail.Application.Core;
using NewRetail.Application.Core.SingleStore;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Dynamic.Core;
using System.Linq.Expressions;
using System.Threading.Tasks;
using Volo.Abp.Application.Dtos;
using Volo.Abp.Application.Services;
using Volo.Abp.Domain.Repositories;

namespace NewRetail.Application.Components.Platform
{
    public class SalesStaticsByCatelogInput : PagingRequestDto
    {
        public string warehouseId { get; set; }
        public string cashierId { get; set; }
        public string cashierName { get; set; }
        public DateTime? startTime { get; set; }
        public DateTime? endTime { get; set; }
    }

    public class SalesStaticsByCatelogResponse : IEntityDto
    {
        public GoodsCategory rootCategory { get; set; }
        public GoodsCategory category { get; set; }
        public PosCashier posCashier { get; set; }

        public Warehouse.Entity.Warehouse Warehouse { get; set; }

        public string warehouseID { get; set; }
        public string categoryId { get; set; }
        public string cashierId { get; set; }
        public decimal amount { get; set; }
        public decimal totalPrice { get; set; }
    }

    public class PayFlowStaticsByPayWayInput : PagingRequestDto
    {
        public string warehouseId { get; set; }
        public DateTime? startTime { get; set; }
        public DateTime? endTime { get; set; }
    }

    public class PayFlowByDateInput : PagingRequestDto
    {
        public string warehouseId { get; set; }
        public DateTime? startTime { get; set; }
        public DateTime? endTime { get; set; }
    }

    public class PayFlowByDateResponse : IEntityDto
    {
        public string warehouseID { get; set; }
        public Warehouse.Entity.Warehouse Warehouse { get; set; }
        public DateTime date { get; set; }
        public decimal salesProfit { get; set; }


        public decimal amount { get; set; }
        public decimal totalPrice { get; set; }
        #region add by cl 20221116
        /// <summary>
        /// 销售金额
        /// </summary>
        public decimal saleAmount { get; set; }
        /// <summary>
        /// 销售数量
        /// </summary>
        public decimal saleQuantity { get; set; }
        /// <summary>
        /// 退货金额
        /// </summary>
        public decimal reSaleAmount { get; set; }
        /// <summary>
        /// Gd退货金额
        /// </summary>
        public decimal reGdAmount { get; set; }
        /// <summary>
        /// 退货数量
        /// </summary>
        public decimal reSaleQuantity { get; set; }
        /// <summary>
        /// 数量小计
        /// </summary>
        public decimal subtotalSaleQuantity { get; set; }
        /// <summary>
        /// 金额小计(付款表的付款金额)
        /// </summary>
        public decimal subtotalSaleMoney { get; set; }
        /// <summary>
        /// 金额小计(销售流水表的销售金额)
        /// </summary>
        public decimal subtotalSaleAmount { get; set; }
        /// <summary>
        /// 抹零金额
        /// </summary>
        public decimal subtotalZeroMoney { get; set; }
        #endregion
    }

    public class PayFlowStaticsByPayWayResponse : IEntityDto
    {
        public string payWay { get; set; }
        public string payWayName { get; set; }
        public string cashierID { get; set; }
        public string warehouseID { get; set; }
        public Warehouse.Entity.Warehouse Warehouse { get; set; }
        public PosCashier PosCashier { get; set; }
        public decimal payAmount { get; set; }
    }

    public class ReportInput : PagingRequestDto
    {
        public string goodsName { get; set; }
        public string goodsNo { get; set; }
        public string warehouseId { get; set; }
        public string supplierId { get; set; }

        public DateTime? startTime { get; set; }
        public DateTime? endTime { get; set; }
    }

    public class ReportStats
    {
        /// <summary>
        /// 数量
        /// </summary>
        public decimal? totalAmount { get; set; }
        /// <summary>
        /// 金额
        /// </summary>
        public decimal? totalPrice { get; set; }
        /// <summary>
        /// Gd金额.只供退货时候使用
        /// </summary>
        public decimal? totalGdPrice { get; set; }
        /// <summary>
        /// 成本价
        /// </summary>
        public decimal? costPrice { get; set; }
        /// <summary>
        /// 成本金额
        /// </summary>
        public decimal? costAmount { get; set; }
    }

    public class ReportResponse : IEntityDto
    {
        public ReportStats InWarehouseData { get; set; }
        public ReportStats OutWarehouseData { get; set; }

        public ReportStats SalesData { get; set; }
        public ReportStats RefundData { get; set; }

        public ReportStats WholeSaleInData { get; set; }
        public ReportStats WholeSaleOutData { get; set; }

        public ReportStats OtherBillInData { get; set; }
        public ReportStats OtherBillOutData { get; set; }

        public ReportStats CheckStockProfitData { get; set; }
        //add by cl 20230213 次品单
        public ReportStats DefectiveData { get; set; }

        ReportStats GetJieYu()
        {
            var amount = (this.InWarehouseData?.totalAmount ?? 0) -
                (this.OutWarehouseData?.totalAmount ?? 0) -
                (this.SalesData?.totalAmount ?? 0) +
                (this.RefundData?.totalAmount ?? 0) -
                (this.WholeSaleOutData?.totalAmount ?? 0) +
                (this.WholeSaleInData?.totalAmount ?? 0) +
                (this.CheckStockProfitData?.totalAmount ?? 0) +
                (this.OtherBillInData?.totalAmount ?? 0) -
                (this.OtherBillOutData?.totalAmount ?? 0) -
                (this.DefectiveData?.totalAmount ?? 0);

            //var price = (this.InWarehouseData?.totalPrice ?? 0) -
            //    (this.OutWarehouseData?.totalPrice ?? 0) -
            //    (this.SalesData?.totalPrice ?? 0) +
            //    (this.RefundData?.totalPrice ?? 0) -
            //    (this.WholeSaleOutData?.totalPrice ?? 0) +
            //    (this.WholeSaleInData?.totalPrice ?? 0) +
            //    (this.CheckStockProfitData?.totalPrice ?? 0) +
            //    (this.OtherBillInData?.totalPrice ?? 0) -
            //    (this.OtherBillOutData?.totalPrice ?? 0);
            var price = (this.InWarehouseData?.costAmount ?? 0) -
                (this.OutWarehouseData?.costAmount ?? 0) -
                (this.SalesData?.costAmount ?? 0) +
                (this.RefundData?.costAmount ?? 0) -
                (this.WholeSaleOutData?.costAmount ?? 0) +
                (this.WholeSaleInData?.costAmount ?? 0) +
                (this.CheckStockProfitData?.costAmount ?? 0) +
                (this.OtherBillInData?.costAmount ?? 0) -
                (this.OtherBillOutData?.costAmount ?? 0) -
                (this.DefectiveData?.costAmount ?? 0);

            return new ReportStats()
            {
                totalAmount = amount,
                totalPrice = price
            };
        }

        public ReportStats JieYu => this.GetJieYu();

        public GoodsInfo GoodsInfo { get; set; }

        public WarehouseGoods WarehouseGoods { get; set; }
    }

    #region add by cl 20221113
    public class SalesDailyCashierInput : PagingRequestDto
    {
        public string warehouseId { get; set; }
        public string cashierId { get; set; }
        public string cashierName { get; set; }
        public DateTime? startTime { get; set; }
        public DateTime? endTime { get; set; }
    }
    public class SalesDailyCashierTotalInput
    {
        public string warehouseId { get; set; }
        public string cashierId { get; set; }
        public string cashierName { get; set; }
        public DateTime? startTime { get; set; }
        public DateTime? endTime { get; set; }
    }
    public class SalesDailyCashierMid : SalesDailyCashierResponse
    {
        public string flowNo { get; set; }
        public string saleWay { get; set; }
    }
    public class SalesDailyCashierResponse : IEntityDto
    {
        public string pKey { get; set; }
        public string payWay { get; set; }
        public string payWayName { get; set; }
        public string cashierID { get; set; }
        public string warehouseID { get; set; }
        public Warehouse.Entity.Warehouse Warehouse { get; set; }
        public PosCashier PosCashier { get; set; }
        public string saleDate { get; set; }
        /// <summary>
        /// 交易笔数
        /// </summary>
        public int tradeNum { get; set; }
        /// <summary>
        /// 销售金额
        /// </summary>
        public decimal saleAmount { get; set; }
        /// <summary>
        /// 销售退货金额
        /// </summary>
        public decimal reSaleAmount { get; set; }
        /// <summary>
        /// 实际金额
        /// </summary>
        public decimal realSaleAmount { get; set; }
        /// <summary>
        /// gd销售金额
        /// </summary>
        public decimal gdAmount { get; set; }
        /// <summary>
        /// gd销售退货金额
        /// </summary>
        public decimal reGdAmount { get; set; }
        /// <summary>
        /// gd实际金额
        /// </summary>
        public decimal realGdAmount { get; set; }
        /// <summary>
        /// 支付方式列表
        /// </summary>
        public ICollection<PosPayFlowRes> PosPayFlows { get; set; }
    }
    public class PosPayFlowRes
    {
        public string saleWay { get; set; }
        public string payWay { get; set; }
        public decimal saleAmount { get; set; }
        public decimal payAmount { get; set; }
    }
    public class SalesDailyCashierTotalResponse
    {
        /// <summary>
        /// 交易笔数
        /// </summary>
        public int tradeNum { get; set; }
        /// <summary>
        /// 销售金额
        /// </summary>
        public decimal saleAmount { get; set; }
        /// <summary>
        /// 销售退货金额
        /// </summary>
        public decimal reSaleAmount { get; set; }
        /// <summary>
        /// gd销售退货金额
        /// </summary>
        public decimal reGdAmount { get; set; }
        /// <summary>
        /// 实际金额
        /// </summary>
        public decimal realSaleAmount { get; set; }
        /// <summary>
        /// gd实际金额
        /// </summary>
        public decimal realGdAmount { get; set; }
        /// <summary>
        /// 支付方式列表
        /// </summary>
        public ICollection<PosPayFlowRes> PosPayFlows { get; set; }
    }
    public class BusinessFlowsInput : PagingRequestDto
    {
        public string warehouseId { get; set; }
        public string categoryId { get; set; }
        public string goodsNo { get; set; }
        public string goodsName { get; set; }
        public string supplierId { get; set; }
        public DateTime? startTime { get; set; }
        public DateTime? endTime { get; set; }
    }
    public class BusinessFlowsResponse : IEntityDto
    {
        public string warehouseID { get; set; }
        //public string categoryId { get; set; }
        //public string categoryNo { get; set; }
        public string supplierId { get; set; }
        public string goodsID { get; set; }
        public string operateTime { get; set; }
        public string businessType { get; set; }
        public string businessReceiptNo { get; set; }
        public decimal quantity { get; set; }
        public decimal price { get; set; }
        public decimal amount { get; set; }

        public Warehouse.Entity.Warehouse Warehouse { get; set; }
        public GoodsCategory rootCategory { get; set; }
        public GoodsCategory category { get; set; }
        public GoodsInfo GoodsInfo { get; set; }
        public Supplier Supplier { get; set; }

    }
    #endregion

    public class PayDayRet
    {
        public string warehouseID { get; set; }
        public int Year { get; set; }
        public int Month { get; set; }
        public int Day { get; set; }
        public decimal amount { get; set; }
        public decimal totalPrice { get; set; }
        public decimal costPrice { get; set; }
        public decimal saleAmount { get; set; }
        public decimal reSaleAmount { get; set; }
        public decimal reGdAmount { get; set; }
        public decimal saleQuantity { get; set; }
        public decimal reSaleQuantity { get; set; }
        public decimal subtotalSaleMoney { get; set; }
        public decimal subtotalPayMoney { get; set; }
        public decimal subtotalSaleQuantity { get; set; }
        /// <summary>
        /// 抹零金额
        /// </summary>
        public decimal subtotalZeroMoney { get; set; }
    }

    public interface IReportService : IApplicationService
    {
        Task<PagedResultDto<ReportResponse>> QueryReport(ReportInput dto);
        Task<PagedResultDto<PayFlowByDateResponse>> PayFlowByDate(PayFlowByDateInput dto);
        Task<PagedResultDto<SalesStaticsByCatelogResponse>> SalesStaticsByCatelog(SalesStaticsByCatelogInput dto);
        Task<PagedResultDto<PayFlowStaticsByPayWayResponse>> PayFlowStaticsByPayWay(PayFlowStaticsByPayWayInput dto);
        #region add by cl 20221113
        Task<PagedResultDto<SalesDailyCashierResponse>> SalesDailyCashier(SalesDailyCashierInput dto);
        Task<SalesDailyCashierTotalResponse> SalesDailyCashierTotal(SalesDailyCashierTotalInput dto);
        #endregion
        #region add by cl 20230205
        Task<PagedResultDto<SalesDailyCashierResponse>> SalesNoDailyCashier(SalesDailyCashierInput dto);
        Task<SalesDailyCashierTotalResponse> SalesNoDailyCashierTotal(SalesDailyCashierTotalInput dto);
        #endregion
        /// <summary>
        /// 商品业务流水
        /// </summary>
        /// <param name="dto"></param>
        /// <returns></returns>
        Task<PagedResultDto<BusinessFlowsResponse>> BusinessFlowsReports(BusinessFlowsInput dto);
        /// <summary>
        /// 门店每日收银，添加付款流水的实收金额
        /// </summary>
        /// <param name="dto"></param>
        /// <returns></returns>
        Task<PagedResultDto<PayFlowByDateResponse>> PayFlowByDateNew(PayFlowByDateInput dto);
        /// <summary>
        /// 门店每日收银，通过查询语句
        /// </summary>
        /// <param name="dto"></param>
        /// <returns></returns>
        Task<PagedResultDto<PayFlowByDateResponse>> PayFlowByDateBySql(PayFlowByDateInput dto);
    }

    public class ReportService : MyApplicationService, IReportService
    {
        private readonly IRepository<PosSalesFlow> posSalesFlows;
        private readonly ISingleStoreService singleStoreService;
        private readonly IMsSqlDbRepository<NewRetail.Application.DataAccess.NewRetailDapperDbContext> _dapperRepository;
        public ReportService(IRepository<PosSalesFlow> posSalesFlows, ISingleStoreService singleStoreService,
            IMsSqlDbRepository<NewRetail.Application.DataAccess.NewRetailDapperDbContext> dapperRepository)
        {
            this.posSalesFlows = posSalesFlows;
            this.singleStoreService = singleStoreService;
            _dapperRepository = dapperRepository;
        }

        /// <summary>
        /// 关联单个供应商
        /// </summary>
        /// <param name="data"></param>
        /// <returns></returns>
        public async Task<WarehouseGoods> AttachSupplier(WarehouseGoods data)
        {
            if (data == null)
            {
                return data;
            }

            string warehouseid = this.singleStoreService.CurrentStore;
            var warehouseGoodsId = $"{data.warehouseID}{data.goodsID}";
            var warehouseGoodsIds = new string[] { warehouseGoodsId };

            var db = await this.posSalesFlows.GetDbContextAsync();
            var inOutWarehousings = await this.QueryInOutWarehousingsByIds(warehouseGoodsIds);
            if (string.IsNullOrEmpty(warehouseid))
            {
                data.Supplier = inOutWarehousings.Where(x => $"{data.warehouseID}{data.goodsID}".Contains($"{x.warehouseID}{x.goodsID}")).Select(s => s.Supplier).FirstOrDefault();
            }
            else
            {
                data.Supplier = inOutWarehousings.Where(x => $"{data.warehouseID}{data.goodsID}".Contains($"{x.warehouseID}{x.goodsID}") && x.warehouseID == warehouseid).Select(s => s.Supplier).FirstOrDefault();
            }

            return data;
        }
        async Task<InOutWarehousing[]> QueryInOutWarehousingsByIds(string[] ids)
        {
            var db = await this.posSalesFlows.GetDbContextAsync();

            var inOutWarehousings = await db.Set<InOutWarehousing>().ToArrayAsync();
            inOutWarehousings = inOutWarehousings.Where(x => ids.Contains($"{x.warehouseID}{x.goodsID}")).ToArray();

            var suppliers = await db.Set<Supplier>().Where(x => inOutWarehousings.Select(x => x.supplierID).Contains(x.Id)).ToArrayAsync();

            foreach (var m in inOutWarehousings)
            {
                m.Supplier = suppliers.FirstOrDefault(x => x.Id == m.supplierID);
            }

            return inOutWarehousings;
        }

        #region 门店每日收银，通过查询语句
        /// <summary>
        /// 门店每日收银，通过查询语句
        /// </summary>
        /// <param name="dto"></param>
        /// <returns></returns>
        public async Task<PagedResultDto<PayFlowByDateResponse>> PayFlowByDateBySql(PayFlowByDateInput dto)
        {
            //单店权限
            string roleWarehouseId = this.singleStoreService.CurrentStore;
            var strSql = @"SELECT * 
                             FROM (SELECT ROW_NUMBER() OVER (ORDER BY tp.operateTime) AS RowNum, tp.warehouseID, tp.operateTime date, 
						                  SUM(tp.salePrice - tp.reSalePrice - tp.costPrice) salesProfit1,
                                          SUM(tp.salesProfit) salesProfit,
						                  SUM(tp.saleQuantity + tp.reSaleQuantity) amount, SUM(tp.salePrice + tp.reSalePrice) totalPrice,
						                  SUM(tp.saleMoney) saleAmount,	SUM(tp.saleQuantity) saleQuantity, SUM(tp.reSaleQuantity) reSaleQuantity,
						                  SUM(tp.reSaleMoney) reSaleAmount, SUM(tp.reGdMoney) reGdAmount,
						                  SUM(tp.saleQuantity - tp.reSaleQuantity) subtotalSaleQuantity,
						                  SUM(pp.payAmount) subtotalSaleMoney, SUM(tp.saleMoney - tp.reSaleMoney) subtotalSaleAmount,
						                  SUM(pm.payAmount) subtotalZeroMoney 
		                             FROM (SELECT ps.flowNo, ps.warehouseID, FORMAT(ps.operateTime, 'yyyy-MM-dd') operateTime,
							                      SUM(CASE WHEN ps.saleWay = 'A' THEN ps.saleQuantity ELSE 0 END) saleQuantity, 
							                      SUM(CASE WHEN ps.saleWay = 'B' THEN ps.saleQuantity ELSE 0 END) reSaleQuantity, 
							                      SUM(CASE WHEN ps.saleWay = 'A' THEN ps.salePrice ELSE 0 END) salePrice, 
							                      SUM(CASE WHEN ps.saleWay = 'B' THEN ps.salePrice ELSE 0 END) reSalePrice, 
							                      SUM(CASE WHEN ps.saleWay = 'A' THEN ps.saleMoney ELSE 0 END) saleMoney, 
							                      SUM(CASE WHEN ps.saleWay = 'B' THEN ps.saleMoney ELSE 0 END) reSaleMoney, 
							                      SUM(CASE WHEN ps.saleWay = 'A' THEN ps.gdMoney ELSE 0 END) gdMoney, 
							                      SUM(CASE WHEN ps.saleWay = 'B' THEN ps.gdMoney ELSE 0 END) reGdMoney,
							                      SUM(CASE WHEN wg.id IS NULL THEN (CASE WHEN ps.saleWay = 'A' THEN ps.salePrice ELSE -ps.salePrice END) ELSE (CASE WHEN ps.saleWay = 'A' THEN (ps.salePrice-wg.costPrice) ELSE -ps.salePrice END) END) AS salesProfit,
							                      AVG(CASE WHEN wg.id IS NULL THEN 0.0 ELSE wg.costPrice END) AS costPrice
							                 FROM pos_sales_flow ps
						                     LEFT JOIN warehouse_goods wg ON wg.warehouseID = ps.warehouseID AND wg.goodsID = ps.goodsID
						                    WHERE 1=1";
            if (!string.IsNullOrWhiteSpace(dto.warehouseId))
                strSql = strSql + $@"         AND ps.warehouseID = '{dto.warehouseId}'";

            if (!string.IsNullOrWhiteSpace(roleWarehouseId))
                strSql = strSql + $@"         AND ps.warehouseID = '{dto.warehouseId}'";

            if (dto.startTime != null)
            {
                dto.startTime = dto.startTime.Value.Date;
                strSql = strSql + $@"         AND ps.operateTime>= '{dto.startTime}'";
            }
            if (dto.endTime != null)
            {
                dto.endTime = dto.endTime.Value.Date.AddDays(1);
                strSql = strSql + $@"         AND ps.operateTime < '{dto.endTime}'";
            }
            strSql = strSql + @"	        GROUP BY ps.flowNo, ps.warehouseID, FORMAT(ps.operateTime, 'yyyy-MM-dd')
					                      ) tp
				                     LEFT JOIN (SELECT flowNo, warehouseID, SUM(CASE WHEN payWay = 'AD' THEN -payAmount ELSE payAmount END) AS payAmount
										          FROM pos_pay_flow
									             GROUP BY flowNo, warehouseID) pp ON pp.flowNo = tp.flowNo AND pp.warehouseID = tp.warehouseID
			                         LEFT JOIN (SELECT flowNo, warehouseID, SUM(CASE WHEN payWay = 'X' THEN payAmount ELSE 0 END) AS payAmount
										          FROM pos_pay_flow
									             GROUP BY flowNo, warehouseID) pm ON pm.flowNo = tp.flowNo AND pm.warehouseID = tp.warehouseID
				                    GROUP BY tp.warehouseID, tp.operateTime
                                  ) tb1";

            var resultRes = await _dapperRepository.GetListAsync<PayFlowByDateResponse>(strSql);


            var count = resultRes.Count();
            int pageSize = dto.PageSize == 0 ? 100 : dto.PageSize, pageIndex = dto.Page == 0 ? 1 : dto.Page;
            //总页数
            var pageCount = (resultRes.Count() + pageSize - 1) / pageSize;
            //var data = groupQuery.Page(pageIndex, pageSize);
            var data = resultRes.Skip((pageIndex - 1) * pageSize).Take(pageSize).ToArray();

            var response = new PagedResultDto<PayFlowByDateResponse>();
            response.TotalCount = count;
            response.Items = data;

            if (!response.Items.Any())
            {
                return response;
            }
            var db = await this.posSalesFlows.GetDbContextAsync();
            await db.AttachData<Warehouse.Entity.Warehouse, PayFlowByDateResponse>(response.Items, x => x.warehouseID,
                (m, arr) => m.Warehouse = arr.FirstOrDefault());

            return response;
        }
        #endregion

        /// <summary>
        /// 门店每日收银，添加付款流水的实收金额
        /// </summary>
        /// <param name="dto"></param>
        /// <returns></returns>
        public async Task<PagedResultDto<PayFlowByDateResponse>> PayFlowByDateNew(PayFlowByDateInput dto)
        {
            var db = await this.posSalesFlows.GetDbContextAsync();
//            var rrse = await _dapperRepository.GetListAsync<dynamic>(@"SELECT flowNo, avg(saleAmount) saleAmount FROM pos_pay_flow WHERE flowNo IN(
//select flowNo from pos_pay_flow where payWay = 'AD' AND payTime >= '2024-01-21 00:00:18.000'
//) group by flowNo;");
            var query = db.Set<PosSalesFlow>().AsNoTracking();

            query = query.WhereIf(!string.IsNullOrWhiteSpace(dto.warehouseId),
                x => x.warehouseID == dto.warehouseId);

            //单店权限
            string roleWarehouseId = this.singleStoreService.CurrentStore;
            query = query.WhereIf(!string.IsNullOrWhiteSpace(roleWarehouseId),
                x => x.warehouseID == roleWarehouseId);

            if (dto.startTime != null)
            {
                dto.startTime = dto.startTime.Value.Date;
                query = query.Where(x => x.operateTime >= dto.startTime);
            }

            if (dto.endTime != null)
            {
                dto.endTime = dto.endTime.Value.Date.AddDays(1);
                query = query.Where(x => x.operateTime < dto.endTime);
            }

            var joinQuery = from saleQuery in query
                            join goodsQuery in db.Set<WarehouseGoods>().AsNoTracking()
                            on new { saleQuery.warehouseID, saleQuery.goodsID } equals new { goodsQuery.warehouseID, goodsQuery.goodsID }
                            into temp
                            from goodsItem in temp.DefaultIfEmpty()
                            select new
                            {
                                saleQuery.flowNo,
                                saleQuery.saleWay,
                                saleQuery.warehouseID,
                                saleQuery.operateTime,
                                saleQuery.saleQuantity,
                                saleQuery.salePrice,
                                saleQuery.saleMoney,
                                saleQuery.gdMoney,
                                costPrice = goodsItem == null ? 0 : goodsItem.costPrice
                            };
            //获取销售流水最终需要统计的单据号
            var pKeys = joinQuery.Select(x => string.Concat(x.flowNo, x.warehouseID)).Distinct().ToList();
            //获取付款表的数据
            var queryPayFlows = db.Set<PosPayFlow>().AsNoTracking().Where(x => pKeys.Contains(string.Concat(x.flowNo, x.warehouseID)));
            //获取实际付款的记录
            var queryResultPayFlows = queryPayFlows.GroupBy(x => new { x.flowNo, x.warehouseID })
                .Select(x => new 
                {
                    flowNo = x.Key.flowNo,
                    warehouseID = x.Key.warehouseID,
                    payAmount = x.Sum(s => s.payWay == "AD" ? -s.payAmount : s.payAmount)
                });

            var groupQuery1 = joinQuery.GroupBy(x => new { x.flowNo, x.saleWay, x.warehouseID, x.operateTime.Year, x.operateTime.Month, x.operateTime.Day })
                .Select(x => new
                {
                    x.Key.flowNo,
                    x.Key.saleWay,
                    x.Key.warehouseID,
                    x.Key.Year,
                    x.Key.Month,
                    x.Key.Day,
                    amount = x.Sum(d => d.saleQuantity),
                    salePrice = x.Sum(d => d.salePrice),
                    costPrice = x.Sum(d => d.costPrice),
                    saleMoney = x.Sum(d => d.saleMoney),
                    gdMoney = x.Sum(d=>d.gdMoney),
                    saleQuantity = x.Sum(d => d.saleQuantity)
                }).ToList();

            var groupQuery = groupQuery1.GroupJoin(queryResultPayFlows, ct => string.Concat(ct.flowNo, ct.warehouseID), cs => string.Concat(cs.flowNo, cs.warehouseID), (x, y) => new { posSalesFlows = x, posPayFlows = y })
               .SelectMany(xy => xy.posPayFlows.DefaultIfEmpty(), (x, y) => new { posSalesFlows = x.posSalesFlows, posPayFlows = y })
               .GroupBy(g => new
               {
                   g.posSalesFlows.warehouseID,
                   g.posSalesFlows.Year,
                   g.posSalesFlows.Month,
                   g.posSalesFlows.Day
               })
                .Select(x => new PayDayRet
                {
                    warehouseID = x.Key.warehouseID,
                    Year = x.Key.Year,
                    Month = x.Key.Month,
                    Day = x.Key.Day,
                    amount = x.Sum(d => d.posSalesFlows.saleQuantity),
                    totalPrice = x.Sum(d => d.posSalesFlows.salePrice),
                    costPrice = x.Sum(d => d.posSalesFlows.costPrice),
                    saleAmount = x.Sum(d => d.posSalesFlows.saleWay == SalesTypes.Sales ? d.posSalesFlows.saleMoney : 0),
                    reSaleAmount = x.Sum(d => d.posSalesFlows.saleWay == SalesTypes.Refunds ? d.posSalesFlows.saleMoney : 0),
                    reGdAmount = x.Sum(d => d.posSalesFlows.saleWay == SalesTypes.Refunds ? d.posSalesFlows.gdMoney : 0),
                    saleQuantity = x.Sum(d => d.posSalesFlows.saleWay == SalesTypes.Sales ? d.posSalesFlows.saleQuantity : 0),
                    reSaleQuantity = x.Sum(d => d.posSalesFlows.saleWay == SalesTypes.Refunds ? d.posSalesFlows.saleQuantity : 0),
                    subtotalSaleMoney = x.Sum(d => d.posSalesFlows.saleWay == SalesTypes.Sales ? d.posSalesFlows.saleMoney : -d.posSalesFlows.saleMoney),
                    subtotalPayMoney = x.Sum(d => d.posSalesFlows.saleWay == SalesTypes.Sales ? d.posPayFlows.payAmount : -d.posPayFlows.payAmount),
                    subtotalSaleQuantity = x.Sum(d => d.posSalesFlows.saleWay == SalesTypes.Sales ? d.posSalesFlows.saleQuantity : -d.posSalesFlows.saleQuantity),
                    subtotalZeroMoney = x.Average(d => d.posSalesFlows.saleWay == SalesTypes.Sales ? queryPayFlows.Where(s => s.payTime.Year == x.Key.Year && s.payTime.Month == x.Key.Month && s.payTime.Day == x.Key.Day && s.warehouseID == x.Key.warehouseID && s.payWay == "X").Sum(a => a.payAmount) : -queryPayFlows.Where(s => s.payTime.Year == x.Key.Year && s.payTime.Month == x.Key.Month && s.payTime.Day == x.Key.Day && s.warehouseID == x.Key.warehouseID && s.payWay == "X").Sum(a => a.payAmount))
                }).ToList();

            var count = groupQuery.Count();
            groupQuery = groupQuery.OrderBy(o => o.Year).ThenBy(o => o.Month).ThenBy(o => o.Day).ToList();
            int pageSize = dto.PageSize == 0 ? 100 : dto.PageSize, pageIndex = dto.Page == 0 ? 1 : dto.Page;
            //总页数
            var pageCount = (groupQuery.Count() + pageSize - 1) / pageSize;
            //var data = groupQuery.Page(pageIndex, pageSize);
            var data = groupQuery.Skip((pageIndex - 1) * pageSize).Take(pageSize).ToArray();

            var response = new PagedResultDto<PayFlowByDateResponse>();
            response.TotalCount = count;
            var Items = data.Select(x => new PayFlowByDateResponse()
            {
                warehouseID = x.warehouseID,
                date = new DateTime(x.Year, x.Month, x.Day).Date,
                amount = x.amount,
                totalPrice = x.totalPrice,
                salesProfit = x.totalPrice - x.costPrice,
                saleAmount = x.saleAmount,
                reSaleAmount = x.reSaleAmount,
                reGdAmount = x.reGdAmount,
                saleQuantity = x.saleQuantity,
                reSaleQuantity = x.reSaleQuantity,
                //subtotalSaleMoney = x.subtotalSaleMoney,
                subtotalSaleMoney = x.subtotalPayMoney,//替换成付款表的付款金额
                subtotalSaleAmount = x.subtotalSaleMoney,
                subtotalSaleQuantity = x.subtotalSaleQuantity,
                subtotalZeroMoney = x.subtotalZeroMoney
            });

            response.Items = Items.ToArray();
            if (!response.Items.Any())
            {
                return response;
            }

            await db.AttachData<Warehouse.Entity.Warehouse, PayFlowByDateResponse>(response.Items, x => x.warehouseID,
                (m, arr) => m.Warehouse = arr.FirstOrDefault());

            return response;
        }

        public async Task<PagedResultDto<PayFlowByDateResponse>> PayFlowByDate(PayFlowByDateInput dto)
        {
            var db = await this.posSalesFlows.GetDbContextAsync();

            var query = db.Set<PosSalesFlow>().AsNoTracking();

            query = query.WhereIf(!string.IsNullOrWhiteSpace(dto.warehouseId),
                x => x.warehouseID == dto.warehouseId);

            //单店权限
            string roleWarehouseId = this.singleStoreService.CurrentStore;
            query = query.WhereIf(!string.IsNullOrWhiteSpace(roleWarehouseId),
                x => x.warehouseID == roleWarehouseId);

            if (dto.startTime != null)
            {
                dto.startTime = dto.startTime.Value.Date;
                query = query.Where(x => x.operateTime >= dto.startTime);
            }

            if (dto.endTime != null)
            {
                dto.endTime = dto.endTime.Value.Date.AddDays(1);
                query = query.Where(x => x.operateTime < dto.endTime);
            }

            var joinQuery = from saleQuery in query
                            join goodsQuery in db.Set<WarehouseGoods>().AsNoTracking()
                            on new { saleQuery.warehouseID, saleQuery.goodsID } equals new { goodsQuery.warehouseID, goodsQuery.goodsID }
                            into temp
                            from goodsItem in temp.DefaultIfEmpty()
                            select new
                            {
                                saleQuery.saleWay,
                                saleQuery.warehouseID,
                                saleQuery.operateTime,
                                saleQuery.saleQuantity,
                                saleQuery.salePrice,
                                saleQuery.saleMoney,
                                costPrice = goodsItem == null ? 0 : goodsItem.costPrice
                            };

            //付款表
            var queryPosPay = db.Set<PosPayFlow>().AsNoTracking();
            var groupQuery = joinQuery.GroupBy(x => new { x.warehouseID, x.operateTime.Year, x.operateTime.Month, x.operateTime.Day })
                .Select(x => new
                {
                    x.Key.warehouseID,
                    x.Key.Year,
                    x.Key.Month,
                    x.Key.Day,
                    amount = x.Sum(d => d.saleQuantity),
                    totalPrice = x.Sum(d => d.salePrice),
                    costPrice = x.Sum(d => d.costPrice),
                    saleAmount = x.Sum(d => d.saleWay == SalesTypes.Sales ? d.saleMoney : 0),
                    reSaleAmount = x.Sum(d => d.saleWay == SalesTypes.Refunds ? d.saleMoney : 0),
                    saleQuantity = x.Sum(d => d.saleWay == SalesTypes.Sales ? d.saleQuantity : 0),
                    reSaleQuantity = x.Sum(d => d.saleWay == SalesTypes.Refunds ? d.saleQuantity : 0),
                    subtotalSaleMoney = x.Sum(d => d.saleWay == SalesTypes.Sales ? d.saleMoney : -d.saleMoney),
                    subtotalSaleQuantity = x.Sum(d => d.saleWay == SalesTypes.Sales ? d.saleQuantity : -d.saleQuantity),
                    subtotalZeroMoney = x.Average(d => d.saleWay == SalesTypes.Sales ? queryPosPay.Where(s => s.payTime.Year == x.Key.Year && s.payTime.Month == x.Key.Month && s.payTime.Day == x.Key.Day && s.warehouseID == x.Key.warehouseID && s.payWay == "X").Sum(a => a.payAmount) : -queryPosPay.Where(s => s.payTime.Year == x.Key.Year && s.payTime.Month == x.Key.Month && s.payTime.Day == x.Key.Day && s.warehouseID == x.Key.warehouseID && s.payWay == "X").Sum(a => a.payAmount))
                });

            var count = await groupQuery.CountAsync();

            var data = await groupQuery.OrderBy(x => x.Year).OrderBy(x => x.Month).OrderBy(x => x.Day)
                .Page(dto.Page, dto.PageSize)
                .ToArrayAsync();

            var response = new PagedResultDto<PayFlowByDateResponse>();
            response.TotalCount = count;
            response.Items = data.Select(x => new PayFlowByDateResponse()
            {
                warehouseID = x.warehouseID,
                date = new DateTime(x.Year, x.Month, x.Day).Date,
                amount = x.amount,
                totalPrice = x.totalPrice,
                salesProfit = x.totalPrice - x.costPrice,
                saleAmount = x.saleAmount,
                reSaleAmount = x.reSaleAmount,
                saleQuantity = x.saleQuantity,
                reSaleQuantity = x.reSaleQuantity,
                subtotalSaleMoney = x.subtotalSaleMoney,
                subtotalSaleAmount = 0,//这边暂时没获取值
                subtotalSaleQuantity = x.subtotalSaleQuantity
            }).ToArray();

            if (!response.Items.Any())
            {
                return response;
            }

            await db.AttachData<Warehouse.Entity.Warehouse, PayFlowByDateResponse>(response.Items, x => x.warehouseID,
                (m, arr) => m.Warehouse = arr.FirstOrDefault());

            return response;
        }

        GoodsCategory FindRoot(GoodsCategory[] data, string currentId)
        {
            var id = currentId;
            GoodsCategory categoryNode = null;

            var ids = new List<string>();

            while (true)
            {
                if (ids.Contains(id))
                {
                    break;
                }
                ids.Add(id);

                var node = data.FirstOrDefault(x => x.Id == id);
                if (node == null)
                {
                    break;
                }

                categoryNode = node;
                id = node.pID;
            }
            return categoryNode;
        }

        public async Task<PagedResultDto<SalesStaticsByCatelogResponse>> SalesStaticsByCatelog(SalesStaticsByCatelogInput dto)
        {
            var db = await this.posSalesFlows.GetDbContextAsync();

            var query = db.Set<PosSalesFlow>().AsNoTracking();

            query = query.WhereIf(!string.IsNullOrWhiteSpace(dto.warehouseId),
                x => x.warehouseID == dto.warehouseId);

            query = query.WhereIf(!string.IsNullOrWhiteSpace(dto.cashierId),
                x => x.cashierID == dto.cashierId);

            //单店权限
            string roleWarehouseId = this.singleStoreService.CurrentStore;
            query = query.WhereIf(!string.IsNullOrWhiteSpace(roleWarehouseId),
                x => x.warehouseID == roleWarehouseId);

            if (!string.IsNullOrWhiteSpace(dto.cashierName))
            {
                var cashierIds = db.Set<PosCashier>().AsNoTracking()
                    .Where(x => x.cashierName.Contains(dto.cashierName))
                    .Select(x => x.Id);
                query = query.Where(x => cashierIds.Contains(x.cashierID));
            }

            if (dto.startTime != null)
            {
                query = query.Where(x => x.operateTime >= dto.startTime);
            }

            if (dto.endTime != null)
            {
                query = query.Where(x => x.operateTime < dto.endTime);
            }

            var groupQuery = query.GroupBy(x => new { x.warehouseID, x.categoryID, x.cashierID })
                .Select(x => new
                {
                    x.Key.warehouseID,
                    x.Key.categoryID,
                    x.Key.cashierID,
                    amount = x.Sum(d => d.saleQuantity),
                    totalPrice = x.Sum(d => d.salePrice)
                });

            var count = await groupQuery.CountAsync();

            var data = await groupQuery.OrderByDescending(x => x.totalPrice)
                .Page(dto.Page, dto.PageSize)
                .ToArrayAsync();

            var response = new PagedResultDto<SalesStaticsByCatelogResponse>();
            response.TotalCount = count;
            response.Items = data.Select(x => new SalesStaticsByCatelogResponse()
            {
                warehouseID = x.warehouseID,
                cashierId = x.cashierID,
                categoryId = x.categoryID,
                amount = x.amount,
                totalPrice = x.totalPrice,
            }).ToArray();

            if (!response.Items.Any())
            {
                return response;
            }

            await db.AttachData<Warehouse.Entity.Warehouse, SalesStaticsByCatelogResponse>(response.Items, x => x.warehouseID,
                (m, arr) => m.Warehouse = arr.FirstOrDefault());

            await db.AttachData<PosCashier, SalesStaticsByCatelogResponse>(response.Items, x => x.cashierId,
                (m, arr) => m.posCashier = arr.FirstOrDefault());

            var categoryData = await db.Set<GoodsCategory>().AsNoTracking().MaxTake().ToArrayAsync();

            foreach (var m in response.Items)
            {
                m.category = categoryData.FirstOrDefault(x => x.Id == m.categoryId);
                m.rootCategory = FindRoot(categoryData, m.categoryId);
            }

            return response;
        }

        /// <summary>
        /// A-现金 Z-支付宝 W-微信 Y-银联  D-会员卡  X-自动抹零  X2-第三方支付 AD-找零
        /// </summary>
        /// <returns></returns>
        public async Task<PagedResultDto<PayFlowStaticsByPayWayResponse>> PayFlowStaticsByPayWay(PayFlowStaticsByPayWayInput dto)
        {
            var db = await this.posSalesFlows.GetDbContextAsync();

            var query = db.Set<PosPayFlow>().AsNoTracking();

            if (!string.IsNullOrWhiteSpace(dto.warehouseId))
            {
                query = query.Where(x => x.warehouseID == dto.warehouseId);
            }

            //单店权限
            string roleWarehouseId = this.singleStoreService.CurrentStore;
            query = query.WhereIf(!string.IsNullOrWhiteSpace(roleWarehouseId),
                x => x.warehouseID == roleWarehouseId);

            if (dto.startTime != null)
            {
                query = query.Where(x => x.payTime >= dto.startTime);
            }

            if (dto.endTime != null)
            {
                query = query.Where(x => x.payTime < dto.endTime);
            }

            var groupQuery = query.GroupBy(x => new { x.warehouseID, x.cashierID, x.payWay })
                .Select(x => new
                {
                    x.Key.warehouseID,
                    x.Key.cashierID,
                    x.Key.payWay,
                    payAmount = x.Sum(d => d.saleAmount)
                });


            var response = new PagedResultDto<PayFlowStaticsByPayWayResponse>();
            response.TotalCount = await groupQuery.CountAsync();
            var data = await groupQuery.OrderByDescending(x => x.payAmount)
                .Page(dto.Page, dto.PageSize).ToArrayAsync();

            response.Items = data.Select(x => new PayFlowStaticsByPayWayResponse()
            {
                warehouseID = x.warehouseID,
                cashierID = x.cashierID,
                payWay = x.payWay,
                payAmount = x.payAmount
            }).ToArray();

            await db.AttachData<PosCashier, PayFlowStaticsByPayWayResponse>(response.Items, x => x.cashierID,
                (m, arr) => m.PosCashier = arr.FirstOrDefault());

            await db.AttachData<Warehouse.Entity.Warehouse, PayFlowStaticsByPayWayResponse>(response.Items, x => x.warehouseID,
                (m, arr) => m.Warehouse = arr.FirstOrDefault());

            var payWayDict = new Dictionary<string, string>()
            {
                ["A"] = "现金",
                ["Z"] = "支付宝",
                ["W"] = "微信",
                ["Y"] = "银联",
                ["D"] = "会员卡",
                ["X"] = "自动抹零",
                ["X2"] = "第三方支付",
                ["AD"] = "找零"
            };

            foreach (var m in response.Items)
            {
                m.payWayName = payWayDict.TryGetValue(m.payWay, out var v) ? v : "unknow";
            }

            return response;
        }

        Expression<Func<ITEM, bool>> ReduceOrCondition<ITEM>(Expression<Func<ITEM, bool>>[] exps)
        {
            exps.Should().NotBeNullOrEmpty();
            if (exps.Count() == 1)
            {
                return exps.First();
            }

            return exps.Aggregate((a, b) => a.Or(b));
        }

        async Task ReportInout(DbContext db, ReportInput dto, ReportResponse[] data)
        {
            var conditions = data.Select(x => x.WarehouseGoods)
                .Select<WarehouseGoods, Expression<Func<InOutWarehousing, bool>>>(x =>
                d => d.warehouseID == x.warehouseID &&
                d.goodsID == x.goodsID).ToArray();

            var con = this.ReduceOrCondition(conditions);

            var query = db.Set<InOutWarehousing>().AsNoTracking().Where(con);
            if (dto.startTime != null)
            {
                query = query.Where(x => x.operateTime >= dto.startTime.Value);
            }
            if (dto.endTime != null)
            {
                query = query.Where(x => x.operateTime < dto.endTime.Value);
            }

            var groupQuery = query.GroupBy(x => new { x.warehouseID, x.goodsID, x.inOrOut }).Select(x => new
            {
                x.Key.warehouseID,
                x.Key.goodsID,
                x.Key.inOrOut,
                count = x.Sum(d => d.quantity),
                price = x.Sum(d => d.price * d.quantity),
                costPrice = x.Average(d => d.price)
            });

            var res = await groupQuery.ToArrayAsync();

            foreach (var m in data)
            {
                var statsData = res.Where(x =>
                  x.warehouseID == m.WarehouseGoods.warehouseID &&
                  x.goodsID == m.WarehouseGoods.goodsID).ToArray();

                m.InWarehouseData = statsData.Where(x => x.inOrOut == (int)InOrOutEnum.IN).Select(x => new ReportStats()
                {
                    totalAmount = x.count,
                    totalPrice = x.price,
                    costAmount = x.price,
                    costPrice = x.costPrice
                }).FirstOrDefault();
                m.OutWarehouseData = statsData.Where(x => x.inOrOut == (int)InOrOutEnum.OUT).Select(x => new ReportStats()
                {
                    totalAmount = x.count,
                    totalPrice = x.price,
                    costAmount = x.price,
                    costPrice = x.costPrice
                }).FirstOrDefault();
            }
        }

        async Task ReportSales(DbContext db, ReportInput dto, ReportResponse[] data)
        {
            var conditions = data.Select(x => x.WarehouseGoods)
                .Select<WarehouseGoods, Expression<Func<PosSalesFlow, bool>>>(x =>
                d => d.warehouseID == x.warehouseID &&
                d.goodsID == x.goodsID).ToArray();

            var con = this.ReduceOrCondition(conditions);

            var query = db.Set<PosSalesFlow>().AsNoTracking().Where(con);
            if (dto.startTime != null)
            {
                query = query.Where(x => x.operateTime >= dto.startTime.Value);
            }
            if (dto.endTime != null)
            {
                query = query.Where(x => x.operateTime < dto.endTime.Value);
            }

            var groupQuery = query.GroupBy(x => new { x.warehouseID, x.goodsID, x.saleWay }).Select(x => new
            {
                x.Key.warehouseID,
                x.Key.goodsID,
                x.Key.saleWay,
                count = x.Sum(d => d.saleQuantity),
                price = x.Sum(d => d.salePrice * d.saleQuantity),
                gdAmount = x.Sum(d=>d.gdMoney)
            });

            var res = await groupQuery.ToArrayAsync();

            foreach (var m in data)
            {
                var statsData = res.Where(x =>
                   x.warehouseID == m.WarehouseGoods.warehouseID &&
                   x.goodsID == m.WarehouseGoods.goodsID).ToArray();

                m.SalesData = statsData.Where(x => x.saleWay == SalesTypes.Sales)
                    .Select(x => new ReportStats()
                    {
                        totalAmount = x.count,
                        totalPrice = x.price,
                        costAmount = x.count * m.InWarehouseData?.costPrice,
                        costPrice = m.InWarehouseData?.costPrice
                    }).FirstOrDefault();
                m.RefundData = statsData.Where(x => x.saleWay == SalesTypes.Refunds)
                    .Select(x => new ReportStats()
                    {
                        totalAmount = x.count,
                        totalPrice = x.price,
                        totalGdPrice = x.gdAmount,
                        costAmount = x.count * m.InWarehouseData?.costPrice,
                        costPrice = m.InWarehouseData?.costPrice
                    }).FirstOrDefault();
            }
        }

        async Task ReportWholeSale(DbContext db, ReportInput dto, ReportResponse[] data)
        {
            var conditions = data.Select(x => x.WarehouseGoods)
                .Select<WarehouseGoods, Expression<Func<WholesaleInOut, bool>>>(x =>
                d => d.warehouseID == x.warehouseID &&
                d.goodsID == x.goodsID).ToArray();

            var con = this.ReduceOrCondition(conditions);

            var query = db.Set<WholesaleInOut>().AsNoTracking().Where(con);
            if (dto.startTime != null)
            {
                query = query.Where(x => x.operateTime >= dto.startTime.Value);
            }
            if (dto.endTime != null)
            {
                query = query.Where(x => x.operateTime < dto.endTime.Value);
            }

            var groupQuery = query.GroupBy(x => new { x.warehouseID, x.goodsID, x.inOrOut }).Select(x => new
            {
                x.Key.warehouseID,
                x.Key.goodsID,
                x.Key.inOrOut,
                count = x.Sum(d => d.quantity),
                price = x.Sum(d => d.price * d.quantity)
            });

            var res = await groupQuery.ToArrayAsync();

            foreach (var m in data)
            {
                var statsData = res.Where(x =>
                   x.warehouseID == m.WarehouseGoods.warehouseID &&
                   x.goodsID == m.WarehouseGoods.goodsID).ToArray();

                m.WholeSaleInData = statsData.Where(x => x.inOrOut == (int)InOrOutEnum.IN)
                    .Select(x => new ReportStats()
                    {
                        totalAmount = x.count,
                        totalPrice = x.price,
                        costAmount = x.count * m.InWarehouseData?.costPrice,
                        costPrice = m.InWarehouseData?.costPrice
                    }).FirstOrDefault();
                m.WholeSaleOutData = statsData.Where(x => x.inOrOut == (int)InOrOutEnum.OUT)
                    .Select(x => new ReportStats()
                    {
                        totalAmount = x.count,
                        totalPrice = x.price,
                        costAmount = x.count * m.InWarehouseData?.costPrice,
                        costPrice = m.InWarehouseData?.costPrice
                    }).FirstOrDefault();
            }
        }

        async Task ReportOtherBill(DbContext db, ReportInput dto, ReportResponse[] data)
        {
            var conditions = data.Select(x => x.WarehouseGoods)
                .Select<WarehouseGoods, Expression<Func<OtherBill, bool>>>(x =>
                d => d.warehouseID == x.warehouseID &&
                d.goodsID == x.goodsID).ToArray();

            var con = this.ReduceOrCondition(conditions);

            var query = db.Set<OtherBill>().AsNoTracking().Where(con);
            if (dto.startTime != null)
            {
                query = query.Where(x => x.operateTime >= dto.startTime.Value);
            }
            if (dto.endTime != null)
            {
                query = query.Where(x => x.operateTime < dto.endTime.Value);
            }

            var groupQuery = query.GroupBy(x => new { x.warehouseID, x.goodsID, x.inOrOut }).Select(x => new
            {
                x.Key.warehouseID,
                x.Key.goodsID,
                x.Key.inOrOut,
                count = x.Sum(d => d.Num),
            });

            var res = await groupQuery.ToArrayAsync();

            foreach (var m in data)
            {
                var statsData = res.Where(x =>
                   x.warehouseID == m.WarehouseGoods.warehouseID &&
                   x.goodsID == m.WarehouseGoods.goodsID).ToArray();

                m.OtherBillInData = statsData.Where(x => x.inOrOut == (int)InOrOutEnum.IN)
                    .Select(x => new ReportStats()
                    {
                        totalAmount = x.count,
                        totalPrice = m.InWarehouseData?.costPrice,
                        costAmount = x.count * m.InWarehouseData?.costPrice,
                        costPrice = m.InWarehouseData?.costPrice
                    }).FirstOrDefault();
                m.OtherBillOutData = statsData.Where(x => x.inOrOut == (int)InOrOutEnum.OUT)
                    .Select(x => new ReportStats()
                    {
                        totalAmount = x.count,
                        totalPrice = m.InWarehouseData?.costPrice,
                        costAmount = x.count * m.InWarehouseData?.costPrice,
                        costPrice = m.InWarehouseData?.costPrice
                    }).FirstOrDefault();
            }
        }

        async Task ReportCheckStock(DbContext db, ReportInput dto, ReportResponse[] data)
        {
            var conditions = data.Select(x => x.WarehouseGoods)
                .Select<WarehouseGoods, Expression<Func<CheckStock, bool>>>(x =>
                d => d.warehouseID == x.warehouseID &&
                d.goodsID == x.goodsID).ToArray();

            var con = this.ReduceOrCondition(conditions);

            var query = db.Set<CheckStock>().AsNoTracking().Where(con);
            if (dto.startTime != null)
            {
                query = query.Where(x => x.operateTime >= dto.startTime.Value);
            }
            if (dto.endTime != null)
            {
                query = query.Where(x => x.operateTime < dto.endTime.Value);
            }

            var groupQuery = query.GroupBy(x => new { x.warehouseID, x.goodsID }).Select(x => new
            {
                x.Key.warehouseID,
                x.Key.goodsID,
                count = x.Sum(d => d.ProfitOrloss),
            });

            var res = await groupQuery.ToArrayAsync();

            foreach (var m in data)
            {
                var statsData = res.Where(x =>
                   x.warehouseID == m.WarehouseGoods.warehouseID &&
                   x.goodsID == m.WarehouseGoods.goodsID).ToArray();

                m.CheckStockProfitData = statsData
                    .Select(x => new ReportStats()
                    {
                        totalAmount = x.count,
                        #region 添加金额
                        totalPrice = m.InWarehouseData?.costPrice,
                        costAmount = x.count * m.InWarehouseData?.costPrice,
                        costPrice = m.InWarehouseData?.costPrice
                        #endregion

                    }).FirstOrDefault();
            }
        }

        /// <summary>
        /// 关联仓库
        /// </summary>
        /// <param name="db"></param>
        /// <param name="dto"></param>
        /// <param name="data"></param>
        /// <returns></returns>
        async Task ReportWarehouse(DbContext db, ReportResponse[] data)
        {
            var warehouseIDs = data.Select(wd => wd.WarehouseGoods).Select(wd => wd.warehouseID);
            var list = await db.Set<Warehouse.Entity.Warehouse>().AsNoTracking()
                .Where(x => warehouseIDs.Contains(x.Id)).ToArrayAsync();
            foreach (var m in data)
            {
                m.WarehouseGoods.Warehouse = list.Where(w => w.Id == m.WarehouseGoods.warehouseID).FirstOrDefault();
                await AttachSupplier(m.WarehouseGoods);
            }
        }

        #region add by cl 20230213
        //次品单
        async Task ReportDefective(DbContext db, ReportInput dto, ReportResponse[] data)
        {
            var conditions = data.Select(x => x.WarehouseGoods)
                .Select<WarehouseGoods, Expression<Func<DefectiveBill, bool>>>(x =>
                d => d.warehouseID == x.warehouseID &&
                d.goodsID == x.goodsID).ToArray();

            var con = this.ReduceOrCondition(conditions);

            var query = db.Set<DefectiveBill>().AsNoTracking().Where(con);
            if (dto.startTime != null)
            {
                query = query.Where(x => x.operateTime >= dto.startTime.Value);
            }
            if (dto.endTime != null)
            {
                query = query.Where(x => x.operateTime < dto.endTime.Value);
            }

            var groupQuery = query.GroupBy(x => new { x.warehouseID, x.goodsID }).Select(x => new
            {
                x.Key.warehouseID,
                x.Key.goodsID,
                count = x.Sum(d => d.quantity),
            });

            var res = await groupQuery.ToArrayAsync();

            foreach (var m in data)
            {
                var statsData = res.Where(x =>
                   x.warehouseID == m.WarehouseGoods.warehouseID &&
                   x.goodsID == m.WarehouseGoods.goodsID).ToArray();

                m.DefectiveData = statsData
                    .Select(x => new ReportStats()
                    {
                        totalAmount = x.count,
                        totalPrice = m.InWarehouseData?.costPrice,
                        costAmount = x.count * m.InWarehouseData?.costPrice,
                        costPrice = m.InWarehouseData?.costPrice
                    }).FirstOrDefault();
            }
        }

        #endregion

        public async Task<PagedResultDto<ReportResponse>> QueryReport(ReportInput dto)
        {
            var db = await this.posSalesFlows.GetDbContextAsync();
            var query = db.Set<WarehouseGoods>().AsNoTracking();
            //查询出产品
            var goodsInfos = db.Set<GoodsInfo>().AsNoTracking().Where(x => x.IsDeleted == false);
            //将已删除的产品过滤掉
            var udgids = goodsInfos.Select(x => x.Id);
            query = query.Where(x => udgids.Contains(x.goodsID));

            if (!string.IsNullOrWhiteSpace(dto.goodsName))
            {
                var gids = goodsInfos.Where(x => x.goodsName.Contains(dto.goodsName)).Select(x => x.Id);
                query = query.Where(x => gids.Contains(x.goodsID));
            }
            if (!string.IsNullOrWhiteSpace(dto.goodsNo))
            {
                var gids = goodsInfos.Where(x => x.No.Contains(dto.goodsNo)).Select(x => x.Id);
                query = query.Where(x => gids.Contains(x.goodsID));
            }
            if (!string.IsNullOrWhiteSpace(dto.warehouseId))
            {
                query = query.Where(x => x.warehouseID == dto.warehouseId);
            }
            //单店权限
            string roleWarehouseId = this.singleStoreService.CurrentStore;
            query = query.WhereIf(!string.IsNullOrWhiteSpace(roleWarehouseId),
                x => x.warehouseID == roleWarehouseId);

            var count = await query.CountAsync();

            var data = await query.OrderByDescending(x => x.addTime)
                .Page(dto.Page, dto.PageSize)
                .ToArrayAsync();

            var response = new PagedResultDto<ReportResponse>();
            response.TotalCount = count;
            response.Items = data.Select(x => new ReportResponse()
            {
                WarehouseGoods = x
            }).ToArray();

            if (response.Items.Any())
            {
                await db.AttachData<GoodsInfo, ReportResponse>(response.Items, x => x.WarehouseGoods.goodsID, (m, arr) => m.GoodsInfo = arr.FirstOrDefault());
                await ReportWarehouse(db, response.Items.ToArray());
                await this.ReportInout(db, dto, response.Items.ToArray());
                await this.ReportSales(db, dto, response.Items.ToArray());
                await this.ReportWholeSale(db, dto, response.Items.ToArray());
                await this.ReportOtherBill(db, dto, response.Items.ToArray());
                await this.ReportCheckStock(db, dto, response.Items.ToArray());
                await this.ReportDefective(db, dto, response.Items.ToArray());
            }

            return response;
        }

        #region add by cl 20221113
        /// <summary>
        /// 收银员每日收银
        /// </summary>
        /// <param name="dto"></param>
        /// <returns></returns>
        public async Task<PagedResultDto<SalesDailyCashierResponse>> SalesDailyCashier(SalesDailyCashierInput dto)
        {
            #region 调用统一方法
            //var db = await this.posSalesFlows.GetDbContextAsync();

            //var query = db.Set<PosSalesFlow>().AsNoTracking();
            //var queryPay = db.Set<PosPayFlow>().AsNoTracking();

            //if (!string.IsNullOrWhiteSpace(dto.warehouseId))
            //{
            //    query = query.Where(x => x.warehouseID == dto.warehouseId);
            //    queryPay = queryPay.Where(x => x.warehouseID == dto.warehouseId);
            //}

            //query = query.WhereIf(!string.IsNullOrWhiteSpace(dto.cashierId),
            //    x => x.cashierID == dto.cashierId);

            ////单店权限
            //string roleWarehouseId = this.singleStoreService.CurrentStore;
            //query = query.WhereIf(!string.IsNullOrWhiteSpace(roleWarehouseId),
            //    x => x.warehouseID == roleWarehouseId);
            //queryPay = queryPay.WhereIf(!string.IsNullOrWhiteSpace(roleWarehouseId),
            //    x => x.warehouseID == roleWarehouseId);

            //if (!string.IsNullOrWhiteSpace(dto.cashierName))
            //{
            //    var cashierIds = db.Set<PosCashier>().AsNoTracking()
            //        .Where(x => x.cashierName.Contains(dto.cashierName))
            //        .Select(x => x.Id);
            //    query = query.Where(x => cashierIds.Contains(x.cashierID));
            //}

            //if (dto.startTime != null)
            //{
            //    query = query.Where(x => x.operateTime >= dto.startTime);
            //}

            //if (dto.endTime != null)
            //{
            //    query = query.Where(x => x.operateTime < dto.endTime);
            //}

            //var groupQuery = query.GroupBy(x => new { x.warehouseID, x.cashierID, saleDate = x.operateTime.Date.ToString(), x.flowNo, x.saleWay })
            //    .Select(x => new SalesDailyCashierMid
            //    {
            //        flowNo = x.Key.flowNo,
            //        saleWay = x.Key.saleWay,
            //        warehouseID = x.Key.warehouseID,
            //        cashierID = x.Key.cashierID,
            //        saleDate = x.Key.saleDate,
            //        saleAmount = x.Sum(d => d.saleWay == SalesTypes.Sales ? d.saleMoney : 0),
            //        reSaleAmount = x.Sum(d => d.saleWay == SalesTypes.Refunds ? d.saleMoney : 0),
            //        tradeNum = x.Select(y => y.flowNo).Distinct().Count(),
            //        PosPayFlows=new List<PosPayFlowRes>()
            //    }).ToArray();

            //foreach(var item in groupQuery)
            //{
            //    var posPayFlows = queryPay.Where(x => x.flowNo == item.flowNo).ToArray();
            //    foreach (var payFlowItem in posPayFlows)
            //    {
            //        item.PosPayFlows.Add(new PosPayFlowRes
            //        {
            //            saleWay = item.saleWay,
            //            payWay = payFlowItem.payWay,
            //            saleAmount = payFlowItem.saleAmount,
            //            payAmount = payFlowItem.payAmount
            //        });
            //    }
            //    //item.PosPayFlows = queryPay.Where(x => x.flowNo == item.flowNo).ToArray();
            //}
            //var resultResponse = groupQuery.GroupBy(x => new { x.warehouseID, x.cashierID, x.saleDate })
            //    .Select(x => new SalesDailyCashierResponse
            //    {
            //        warehouseID = x.Key.warehouseID,
            //        cashierID = x.Key.cashierID,
            //        saleDate = x.Key.saleDate,
            //        saleAmount = x.Sum(d => d.saleAmount),
            //        reSaleAmount = x.Sum(d => d.reSaleAmount),
            //        realSaleAmount = x.Sum(d => d.saleAmount - d.reSaleAmount),
            //        tradeNum = x.Sum(c => c.tradeNum),
            //        //PosPayFlows = x.GroupBy(g=>new { g.payWay}).Select(m => new { aa= x.Key, bb=m.PosPayFlows }).GroupBy(p => new { p. })
            //        // .Select(s => new PosPayFlow
            //        // {
            //        //     payWay = s.Key.payWay,
            //        //     saleAmount = s.Sum(a => a.saleAmount),
            //        //     payAmount = s.Sum(a => a.payAmount)
            //        // }).ToArray()

            //        PosPayFlows = x.SelectMany(m => m.PosPayFlows).GroupBy(p => new { p.payWay })
            //        .Select(s => new PosPayFlowRes
            //        {
            //            payWay = s.Key.payWay,
            //            saleAmount = s.Sum(a => a.saleAmount),
            //            payAmount = s.Sum(a => (a.saleWay == "B" && a.payWay == "A") ? -a.payAmount : a.payAmount)
            //        }).ToArray()
            //        //PosPayFlows = x.SelectMany(m => m.PosPayFlows).GroupBy(p => new { p.payWay })
            //        //.Select(s => new
            //        //{
            //        //    payWay = s.Key.payWay,
            //        //    saleAmount = s.Average(a => a.saleAmount),
            //        //    payAmount = s.Sum(a => a.payAmount)
            //        //}).ToArray()
            //    }).ToArray();
            ////遍历付款方式，将现金中去掉找零的金额
            //foreach(var item in resultResponse)
            //{
            //    //找零付款金额
            //    var adPayAmount = item.PosPayFlows.FirstOrDefault(x => x.payWay == "AD") == null ? 0 : item.PosPayFlows.FirstOrDefault(x => x.payWay == "AD").payAmount;
            //    foreach(var payItem in item.PosPayFlows)
            //    {
            //        //如果是现金，那么需要减掉找零金额
            //        if (payItem.payWay == "A")
            //        {
            //            payItem.payAmount = payItem.payAmount - adPayAmount;
            //            break;
            //        }
            //    }
            //}
            #endregion

            var db = await this.posSalesFlows.GetDbContextAsync();
            var inputDto = new SalesDailyCashierTotalInput
            {
                warehouseId = dto?.warehouseId,
                cashierId = dto?.cashierId,
                cashierName = dto?.cashierName,
                startTime = dto?.startTime,
                endTime = dto?.endTime
            };

            var resultResponse = await SalesDailyCashierData(inputDto);

            var response = new PagedResultDto<SalesDailyCashierResponse>();
            response.TotalCount = resultResponse.Count();
            //var ret = await resultResponse.CountAsync();
            //var data = await resultResponse.OrderByDescending(x => x.saleDate)
            //    .Page(dto.Page, dto.PageSize).ToArrayAsync();
            var data = resultResponse.OrderByDescending(x => x.saleDate).ToArray();

            int pageSize = dto.PageSize, pageIndex = dto.Page;
            //总页数
            var pageCount = (data.Count() + pageSize - 1) / pageSize;
            data = data.Skip((pageIndex - 1) * pageSize).Take(pageSize).ToArray();
            //添加唯一值
            foreach(var item in data)
            {
                item.pKey = Guid.NewGuid().ToString("N");
            }
            response.Items = data;

            await db.AttachData<PosCashier, SalesDailyCashierResponse>(response.Items, x => x.cashierID,
                (m, arr) => m.PosCashier = arr.FirstOrDefault());

            await db.AttachData<Warehouse.Entity.Warehouse, SalesDailyCashierResponse>(response.Items, x => x.warehouseID,
                (m, arr) => m.Warehouse = arr.FirstOrDefault());

            return response;
        }

        public async Task<SalesDailyCashierTotalResponse> SalesDailyCashierTotal(SalesDailyCashierTotalInput dto)
        {
            var db = await this.posSalesFlows.GetDbContextAsync();

            var resultResponse = await SalesDailyCashierData(dto);

            var result = new SalesDailyCashierTotalResponse();
            result.tradeNum = resultResponse.Sum(x => x.tradeNum);
            result.saleAmount = resultResponse.Sum(x => x.saleAmount);
            result.reSaleAmount = resultResponse.Sum(x => x.reSaleAmount);
            result.realSaleAmount = resultResponse.Sum(x => x.realSaleAmount);
            result.reGdAmount = resultResponse.Sum(x => x.reGdAmount);
            result.realGdAmount = resultResponse.Sum(x => x.realGdAmount);
            List<PosPayFlowRes> posPayFlowList = new List<PosPayFlowRes>();
            foreach(var item in resultResponse)
            {
                posPayFlowList.AddRange(item.PosPayFlows);
            }
            var posPayFlowResult = posPayFlowList.GroupBy(g => g.payWay).Select(x => new PosPayFlowRes
            {
                payWay = x.Key,
                saleAmount = x.Sum(s => s.saleAmount),
                payAmount = x.Sum(s => s.payAmount)
            });
            result.PosPayFlows = posPayFlowResult.ToList();
            return result;
        }
        #endregion

        #region update by cl 20230205
        /// <summary>
        /// 统一获取销售数据
        /// </summary>
        /// <param name="dto"></param>
        /// <param name="gType">类型：如果为1则返回结果需要根据日期分组，如果为2那么去掉日期的分组统计。默认为1</param>
        /// <returns></returns>
        private async Task<IEnumerable<SalesDailyCashierResponse>> SalesDailyCashierData(SalesDailyCashierTotalInput dto, int gType = 1)
        {
            var db = await this.posSalesFlows.GetDbContextAsync();

            var query = db.Set<PosSalesFlow>().AsNoTracking();
            var queryPay = db.Set<PosPayFlow>().AsNoTracking();

            if (!string.IsNullOrWhiteSpace(dto.warehouseId))
            {
                query = query.Where(x => x.warehouseID == dto.warehouseId);
                queryPay = queryPay.Where(x => x.warehouseID == dto.warehouseId);
            }

            query = query.WhereIf(!string.IsNullOrWhiteSpace(dto.cashierId),
                x => x.cashierID == dto.cashierId);

            //单店权限
            string roleWarehouseId = this.singleStoreService.CurrentStore;
            query = query.WhereIf(!string.IsNullOrWhiteSpace(roleWarehouseId),
                x => x.warehouseID == roleWarehouseId);
            queryPay = queryPay.WhereIf(!string.IsNullOrWhiteSpace(roleWarehouseId),
                x => x.warehouseID == roleWarehouseId);

            if (!string.IsNullOrWhiteSpace(dto.cashierName))
            {
                var cashierIds = db.Set<PosCashier>().AsNoTracking()
                    .Where(x => x.cashierName.Contains(dto.cashierName))
                    .Select(x => x.Id);
                query = query.Where(x => cashierIds.Contains(x.cashierID));
            }

            if (dto.startTime != null)
            {
                query = query.Where(x => x.operateTime >= dto.startTime);
            }

            if (dto.endTime != null)
            {
                query = query.Where(x => x.operateTime < dto.endTime);
            }
            //var result = await query.GroupJoin(queryPay, ct => ct.flowNo, cs => cs.flowNo, (x, y) => new { posSalesFlow = x, posPayFlows = y })
            //    .SelectMany(xy => xy.posPayFlows.DefaultIfEmpty(), (xy, p) => new
            //    {
            //        flowNo = xy.posSalesFlow.flowNo,
            //        saleWay = xy.posSalesFlow.saleWay,
            //        warehouseID = xy.posSalesFlow.warehouseID,
            //        cashierID = xy.posSalesFlow.cashierID,
            //        saleDate = xy.posSalesFlow.operateTime.Date.ToString(),
            //        saleMoney = xy.posSalesFlow.saleMoney,
            //        payWay = p.payWay,
            //        saleAmount = p.saleAmount,
            //        payAmount = p.payAmount,
            //    }).ToArrayAsync();
            //var result1 = result
            //    .GroupBy(g => new
            //    {
            //        g.warehouseID,
            //        g.cashierID,
            //        g.saleDate
            //    })
            //    .Select(s => new
            //    {
            //        warehouseID = s.Key.warehouseID,
            //        cashierID = s.Key.cashierID,
            //        saleDate = s.Key.saleDate,
            //        saleAmount = s.Sum(d => d.saleWay == SalesTypes.Sales ? d.saleMoney : 0),
            //        reSaleAmount = s.Sum(d => d.saleWay == SalesTypes.Refunds ? d.saleMoney : 0),
            //        realSaleAmount = s.Sum(d => d.saleWay == SalesTypes.Sales ? d.saleMoney : -d.saleMoney),

            //        PosPayFlows = s.Select(d => new
            //        {
            //            saleWay = d.saleWay,
            //            payWay = d.payWay,
            //            saleAmount = d.saleAmount,
            //            payAmount = d.payAmount
            //        }).ToArray()
            //        //.GroupBy(p => new { p.payWay })
            //        //.Select(s => new PosPayFlow
            //        //{
            //        //    payWay = s.Key.payWay,
            //        //    //saleAmount = s.Average(a => a.saleAmount),
            //        //    payAmount = s.Sum(a => a.saleWay == "A" ? a.payAmount : -a.payAmount),
            //        //    //payAmount = s.Sum(a => s.Key.payWay == "AD" ? a.payAmount : (((a.saleWay == "B" && s.Key.payWay == "A") || (a.saleWay == "A" && s.Key.payWay == "AD")) ? -a.payAmount : a.payAmount)),
            //        //}).ToArray()

            //    });

            var groupQuery = query.GroupBy(x => new { x.warehouseID, x.cashierID, saleDate = x.operateTime.Date.ToString(), x.flowNo })//, x.saleWay })
                .Select(x => new SalesDailyCashierMid
                {
                    flowNo = x.Key.flowNo,
                    //saleWay = x.Key.saleWay,
                    saleWay = x.Max(m => m.flowNo.Substring(0, 1) == "H" ? "A" : m.saleWay),//由于换货里面既有销售又有退货，因此如果是换货直接就当作是销售
                    warehouseID = x.Key.warehouseID,
                    cashierID = x.Key.cashierID,
                    saleDate = x.Key.saleDate,
                    saleAmount = x.Sum(d => d.saleWay == SalesTypes.Sales ? d.saleMoney : 0),
                    reSaleAmount = x.Sum(d => d.saleWay == SalesTypes.Refunds ? d.saleMoney : 0),
                    gdAmount = x.Sum(d => d.saleWay == SalesTypes.Sales ? d.gdMoney : 0),
                    reGdAmount = x.Sum(d => d.saleWay == SalesTypes.Refunds ? d.gdMoney : 0),
                    tradeNum = x.Select(y => y.flowNo).Distinct().Count(),
                    PosPayFlows = new List<PosPayFlowRes>()
                }).ToArray();

            foreach (var item in groupQuery)
            {
                var posPayFlows = queryPay.Where(x => x.flowNo == item.flowNo).ToArray();
                foreach (var payFlowItem in posPayFlows)
                {
                    item.PosPayFlows.Add(new PosPayFlowRes
                    {
                        saleWay = item.saleWay,
                        payWay = payFlowItem.payWay,
                        saleAmount = payFlowItem.saleAmount,
                        payAmount = payFlowItem.payAmount
                    });
                }
                //item.PosPayFlows = queryPay.Where(x => x.flowNo == item.flowNo).ToArray();
            }
            var resultResponse = Array.Empty<SalesDailyCashierResponse>();
            if (gType == 1)
            {
                resultResponse = groupQuery.GroupBy(x => new { x.warehouseID, x.cashierID, x.saleDate })
                    .Select(x => new SalesDailyCashierResponse
                    {
                        warehouseID = x.Key.warehouseID,
                        cashierID = x.Key.cashierID,
                        saleDate = x.Key.saleDate,
                        saleAmount = x.Sum(d => d.saleAmount),
                        reSaleAmount = x.Sum(d => d.reSaleAmount),
                        realSaleAmount = x.Sum(d => d.saleAmount - d.reSaleAmount),
                        gdAmount = x.Sum(d => d.gdAmount),
                        reGdAmount = x.Sum(d => d.reGdAmount),
                        realGdAmount = x.Sum(d => d.gdAmount - d.reGdAmount),
                        tradeNum = x.Sum(c => c.tradeNum),
                        //PosPayFlows = x.GroupBy(g=>new { g.payWay}).Select(m => new { aa= x.Key, bb=m.PosPayFlows }).GroupBy(p => new { p. })
                        // .Select(s => new PosPayFlow
                        // {
                        //     payWay = s.Key.payWay,
                        //     saleAmount = s.Sum(a => a.saleAmount),
                        //     payAmount = s.Sum(a => a.payAmount)
                        // }).ToArray()

                        PosPayFlows = x.SelectMany(m => m.PosPayFlows).GroupBy(p => new { p.payWay })
                        .Select(s => new PosPayFlowRes
                        {
                            payWay = s.Key.payWay,
                            saleAmount = s.Sum(a => a.saleAmount),
                            //由于退货纯现金，以及退货的抹零也要处理
                            payAmount = s.Sum(a => (a.saleWay == "B" && a.payWay == "A") || (a.saleWay == "B" && a.payWay == "X") ? -a.payAmount : a.payAmount)
                        }).ToArray()
                        //PosPayFlows = x.SelectMany(m => m.PosPayFlows).GroupBy(p => new { p.payWay })
                        //.Select(s => new
                        //{
                        //    payWay = s.Key.payWay,
                        //    saleAmount = s.Average(a => a.saleAmount),
                        //    payAmount = s.Sum(a => a.payAmount)
                        //}).ToArray()
                    }).ToArray();
            }
            else if (gType == 2)
            {
                resultResponse = groupQuery.GroupBy(x => new { x.warehouseID, x.cashierID })
                    .Select(x => new SalesDailyCashierResponse
                    {
                        warehouseID = x.Key.warehouseID,
                        cashierID = x.Key.cashierID,
                        saleAmount = x.Sum(d => d.saleAmount),
                        reSaleAmount = x.Sum(d => d.reSaleAmount),
                        realSaleAmount = x.Sum(d => d.saleAmount - d.reSaleAmount),
                        gdAmount = x.Sum(d => d.gdAmount),
                        reGdAmount = x.Sum(d => d.reGdAmount),
                        realGdAmount = x.Sum(d => d.gdAmount - d.reGdAmount),
                        tradeNum = x.Sum(c => c.tradeNum),

                        PosPayFlows = x.SelectMany(m => m.PosPayFlows).GroupBy(p => new { p.payWay })
                        .Select(s => new PosPayFlowRes
                        {
                            payWay = s.Key.payWay,
                            saleAmount = s.Sum(a => a.saleAmount),
                            //由于退货纯现金，以及退货的抹零也要处理
                            payAmount = s.Sum(a => (a.saleWay == "B" && a.payWay == "A") || (a.saleWay == "B" && a.payWay == "X") ? -a.payAmount : a.payAmount)
                        }).ToArray()
                    }).ToArray();
            }
            //遍历付款方式，将现金中去掉找零的金额
            foreach (var item in resultResponse)
            {
                //找零付款金额
                var adPayAmount = item.PosPayFlows.FirstOrDefault(x => x.payWay == "AD") == null ? 0 : item.PosPayFlows.FirstOrDefault(x => x.payWay == "AD").payAmount;
                foreach (var payItem in item.PosPayFlows)
                {
                    //如果是现金，那么需要减掉找零金额
                    if (payItem.payWay == "A")
                    {
                        payItem.payAmount = payItem.payAmount - adPayAmount;
                        break;
                    }
                }
                //实收金额重新赋值，直接将付款方式的相加
                item.realSaleAmount = 0;
                foreach(var payItem in item.PosPayFlows)
                {
                    if (payItem.payWay != "X" && payItem.payWay != "AD")
                        item.realSaleAmount += payItem.payAmount;
                }
            }
            return resultResponse;
        }
        #endregion

        #region add by cl 20230205
        /// <summary>
        /// 收银员每日收银-去掉返回值中的日期字段后的结果
        /// </summary>
        /// <param name="dto"></param>
        /// <returns></returns>
        public async Task<PagedResultDto<SalesDailyCashierResponse>> SalesNoDailyCashier(SalesDailyCashierInput dto)
        {
            var db = await this.posSalesFlows.GetDbContextAsync();
            var inputDto = new SalesDailyCashierTotalInput
            {
                warehouseId = dto?.warehouseId,
                cashierId = dto?.cashierId,
                cashierName = dto?.cashierName,
                startTime = dto?.startTime,
                endTime = dto?.endTime
            };

            var resultResponse = await SalesDailyCashierData(inputDto, 2);

            var response = new PagedResultDto<SalesDailyCashierResponse>();
            response.TotalCount = resultResponse.Count();
            var data = resultResponse.OrderByDescending(x => x.saleDate).ToArray();

            int pageSize = dto.PageSize, pageIndex = dto.Page;
            //总页数
            var pageCount = (data.Count() + pageSize - 1) / pageSize;
            data = data.Skip((pageIndex - 1) * pageSize).Take(pageSize).ToArray();
            //添加唯一值
            foreach (var item in data)
            {
                item.pKey = Guid.NewGuid().ToString("N");
            }
            response.Items = data;

            await db.AttachData<PosCashier, SalesDailyCashierResponse>(response.Items, x => x.cashierID,
                (m, arr) => m.PosCashier = arr.FirstOrDefault());

            await db.AttachData<Warehouse.Entity.Warehouse, SalesDailyCashierResponse>(response.Items, x => x.warehouseID,
                (m, arr) => m.Warehouse = arr.FirstOrDefault());

            return response;
        }

        /// <summary>
        /// 合计
        /// </summary>
        /// <param name="dto"></param>
        /// <returns></returns>
        public async Task<SalesDailyCashierTotalResponse> SalesNoDailyCashierTotal(SalesDailyCashierTotalInput dto)
        {
            var db = await this.posSalesFlows.GetDbContextAsync();

            var resultResponse = await SalesDailyCashierData(dto, 2);

            var result = new SalesDailyCashierTotalResponse();
            result.tradeNum = resultResponse.Sum(x => x.tradeNum);
            result.saleAmount = resultResponse.Sum(x => x.saleAmount);
            result.reSaleAmount = resultResponse.Sum(x => x.reSaleAmount);
            result.realSaleAmount = resultResponse.Sum(x => x.realSaleAmount);
            result.reGdAmount = resultResponse.Sum(x => x.reGdAmount);
            result.realGdAmount = resultResponse.Sum(x => x.realGdAmount);
            List<PosPayFlowRes> posPayFlowList = new List<PosPayFlowRes>();
            foreach (var item in resultResponse)
            {
                posPayFlowList.AddRange(item.PosPayFlows);
            }
            var posPayFlowResult = posPayFlowList.GroupBy(g => g.payWay).Select(x => new PosPayFlowRes
            {
                payWay = x.Key,
                saleAmount = x.Sum(s => s.saleAmount),
                payAmount = x.Sum(s => s.payAmount)
            });
            result.PosPayFlows = posPayFlowResult.ToList();
            return result;
        }

        #endregion

        #region add by cl 20221124
        /// <summary>
        /// 商品业务流水
        /// </summary>
        /// <param name="dto"></param>
        /// <returns></returns>
        public async Task<PagedResultDto<BusinessFlowsResponse>> BusinessFlowsReports(BusinessFlowsInput dto)
        {
            var db = await this.posSalesFlows.GetDbContextAsync();
            //出入库
            var queryInOutWarehousing = db.Set<InOutWarehousing>().AsNoTracking();
            //销售
            var queryPosSalesFlow = db.Set<PosSalesFlow>().AsNoTracking();
            //批发出入
            var queryWholesaleInOut = db.Set<WholesaleInOut>().AsNoTracking();
            //盘点
            var queryCheckStock = db.Set<CheckStock>().AsNoTracking();
            //其他单
            var queryOtherBill = db.Set<OtherBill>().AsNoTracking();
            //次品单
            var queryDefectiveBill = db.Set<DefectiveBill>().AsNoTracking();

            if (!string.IsNullOrWhiteSpace(dto.warehouseId))
            {
                queryInOutWarehousing = queryInOutWarehousing.Where(x => x.warehouseID == dto.warehouseId);
                queryPosSalesFlow = queryPosSalesFlow.Where(x => x.warehouseID == dto.warehouseId);
                queryWholesaleInOut = queryWholesaleInOut.Where(x => x.warehouseID == dto.warehouseId);
                queryCheckStock = queryCheckStock.Where(x => x.warehouseID == dto.warehouseId);
                queryOtherBill = queryOtherBill.Where(x => x.warehouseID == dto.warehouseId);
                queryDefectiveBill = queryDefectiveBill.Where(x => x.warehouseID == dto.warehouseId);
            }
            if (!string.IsNullOrWhiteSpace(dto.goodsName))
            {
                var gids = db.Set<GoodsInfo>().AsNoTracking().Where(x => x.goodsName.Contains(dto.goodsName)).Select(x => x.Id);
                queryInOutWarehousing = queryInOutWarehousing.Where(x => gids.Contains(x.goodsID));
                queryPosSalesFlow = queryPosSalesFlow.Where(x => gids.Contains(x.goodsID));
                queryWholesaleInOut = queryWholesaleInOut.Where(x => gids.Contains(x.goodsID));
                queryCheckStock = queryCheckStock.Where(x => gids.Contains(x.goodsID));
                queryOtherBill = queryOtherBill.Where(x => gids.Contains(x.goodsID));
                queryDefectiveBill = queryDefectiveBill.Where(x => gids.Contains(x.goodsID));
            }
            if (!string.IsNullOrWhiteSpace(dto.goodsNo))
            {
                var gids = db.Set<GoodsInfo>().AsNoTracking().Where(x => x.No.Contains(dto.goodsNo)).Select(x => x.Id);
                queryInOutWarehousing = queryInOutWarehousing.Where(x => gids.Contains(x.goodsID));
                queryPosSalesFlow = queryPosSalesFlow.Where(x => gids.Contains(x.goodsID));
                queryWholesaleInOut = queryWholesaleInOut.Where(x => gids.Contains(x.goodsID));
                queryCheckStock = queryCheckStock.Where(x => gids.Contains(x.goodsID));
                queryOtherBill = queryOtherBill.Where(x => gids.Contains(x.goodsID));
                queryDefectiveBill = queryDefectiveBill.Where(x => gids.Contains(x.goodsID));
            }

            //queryInOutWarehousing = queryInOutWarehousing.WhereIf(!string.IsNullOrWhiteSpace(dto.categoryId),
            //    x => x.categoryID == dto.categoryId);

            ////单店权限
            //string roleWarehouseId = this.singleStoreService.CurrentStore;
            //query = query.WhereIf(!string.IsNullOrWhiteSpace(roleWarehouseId),
            //    x => x.warehouseID == roleWarehouseId);
            //queryPay = queryPay.WhereIf(!string.IsNullOrWhiteSpace(roleWarehouseId),
            //    x => x.warehouseID == roleWarehouseId);

            if (dto.startTime != null)
            {
                queryInOutWarehousing = queryInOutWarehousing.Where(x => x.operateTime >= dto.startTime);
                queryPosSalesFlow = queryPosSalesFlow.Where(x => x.operateTime >= dto.startTime);
                queryWholesaleInOut = queryWholesaleInOut.Where(x => x.operateTime >= dto.startTime);
                queryCheckStock = queryCheckStock.Where(x => x.operateTime >= dto.startTime);
                queryOtherBill = queryOtherBill.Where(x => x.operateTime >= dto.startTime);
                queryDefectiveBill = queryDefectiveBill.Where(x => x.operateTime >= dto.startTime);
            }

            if (dto.endTime != null)
            {
                queryInOutWarehousing = queryInOutWarehousing.Where(x => x.operateTime < dto.endTime);
                queryPosSalesFlow = queryPosSalesFlow.Where(x => x.operateTime < dto.endTime);
                queryWholesaleInOut = queryWholesaleInOut.Where(x => x.operateTime < dto.endTime);
                queryCheckStock = queryCheckStock.Where(x => x.operateTime < dto.endTime);
                queryOtherBill = queryOtherBill.Where(x => x.operateTime < dto.endTime);
                queryDefectiveBill = queryDefectiveBill.Where(x => x.operateTime < dto.endTime);
            }
            //出入库
            var inOutWarehousings = queryInOutWarehousing.GroupBy(g => new
            {
                g.warehouseID,
                g.goodsID,
                g.inOrOut,
                businessReceiptNo = g.receiptNo,
                operateTime = g.operateTime.Value.Date
            }).Select(x => new BusinessFlowsResponse
            {
                warehouseID = x.Key.warehouseID,
                goodsID = x.Key.goodsID,
                operateTime = x.Key.operateTime.Date.ToString("yyyy-MM-dd"),
                businessType = x.Key.inOrOut == (int)InOrOutEnum.IN ? "采购入库" : "采购出库",
                businessReceiptNo = x.Key.businessReceiptNo,
                quantity = x.Sum(s => s.quantity),
                price = x.Average(s => s.price),
                amount = x.Sum(s => s.quantity * s.price)
            }).ToList();
            //销售
            var posSalesFlows = queryPosSalesFlow.GroupBy(g => new
            {
                g.warehouseID,
                g.goodsID,
                g.saleWay,
                businessReceiptNo = g.flowNo,
                operateTime = g.operateTime.Date
            }).Select(x => new BusinessFlowsResponse
            {
                warehouseID = x.Key.warehouseID,
                goodsID = x.Key.goodsID,
                operateTime = x.Key.operateTime.Date.ToString("yyyy-MM-dd"),
                businessType = x.Key.saleWay == (string)SalesTypes.Refunds ? "零售退库" : "零售出库",
                businessReceiptNo = x.Key.businessReceiptNo,
                quantity = x.Sum(s => s.saleQuantity),
                price = x.Average(s => s.salePrice),
                amount = x.Sum(s => s.saleMoney)
            }).ToList();
            //批发
            var wholesaleInOuts = queryWholesaleInOut.GroupBy(g => new
            {
                g.warehouseID,
                g.goodsID,
                g.inOrOut,
                businessReceiptNo = g.receiptNo,
                operateTime = g.operateTime.Value.Date
            }).Select(x => new BusinessFlowsResponse
            {
                warehouseID = x.Key.warehouseID,
                goodsID = x.Key.goodsID,
                operateTime = x.Key.operateTime.Date.ToString("yyyy-MM-dd"),
                businessType = x.Key.inOrOut == (int)InOrOutEnum.IN ? "批发退库" : "批发出库",
                businessReceiptNo = x.Key.businessReceiptNo,
                quantity = x.Sum(s => s.quantity),
                price = x.Average(s => s.price),
                amount = x.Sum(s => s.quantity * s.price)
            }).ToList();
            //盘点
            var checkStocks = queryCheckStock.GroupBy(g => new
            {
                g.warehouseID,
                g.goodsID,
                businessReceiptNo = g.receiptNo,
                operateTime = g.operateTime.Value.Date
            }).Select(x => new BusinessFlowsResponse
            {
                warehouseID = x.Key.warehouseID,
                goodsID = x.Key.goodsID,
                operateTime = x.Key.operateTime.Date.ToString("yyyy-MM-dd"),
                businessType = "盘点",
                businessReceiptNo = x.Key.businessReceiptNo,
                quantity = x.Sum(s => s.newNum),
                price = 0,
                amount = 0
            }).ToList();
            //其他单
            var otherBills = queryOtherBill.GroupBy(g => new
            {
                g.warehouseID,
                g.goodsID,
                g.inOrOut,
                businessReceiptNo = g.receiptNo,
                operateTime = g.operateTime.Value.Date
            }).Select(x => new BusinessFlowsResponse
            {
                warehouseID = x.Key.warehouseID,
                goodsID = x.Key.goodsID,
                operateTime = x.Key.operateTime.Date.ToString("yyyy-MM-dd"),
                businessType = x.Key.inOrOut == (int)InOrOutEnum.IN ? "其他单入库" : "其他单出库",
                businessReceiptNo = x.Key.businessReceiptNo,
                quantity = x.Sum(s => s.Num),
                price = 0,
                amount = 0
            }).ToList();
            //次品单
            var defectiveBills = queryDefectiveBill.GroupBy(g => new
            {
                g.warehouseID,
                g.goodsID,
                businessReceiptNo = g.receiptNo,
                operateTime = g.operateTime.Value.Date
            }).Select(x => new BusinessFlowsResponse
            {
                warehouseID = x.Key.warehouseID,
                goodsID = x.Key.goodsID,
                operateTime = x.Key.operateTime.Date.ToString("yyyy-MM-dd"),
                businessType = "次品单",
                businessReceiptNo = x.Key.businessReceiptNo,
                quantity = x.Sum(s => s.quantity),
                price = x.Average(s => s.price),
                amount = x.Sum(s => s.quantity * s.price)
            }).ToList();

            //由于盘点和其他单没单价，因此这边重新计算单价和金额
            foreach (var item in checkStocks)
            {
                item.price = inOutWarehousings.Where(x => x.warehouseID == item.warehouseID && x.goodsID == item.goodsID).OrderByDescending(o => o.operateTime).FirstOrDefault()?.price ?? db.Set<GoodsInfo>().AsNoTracking().Where(x => x.Id == item.goodsID).FirstOrDefault()?.price ?? 0;
                item.amount = item.price * item.quantity;
            }
            foreach (var item in otherBills)
            {
                item.price = inOutWarehousings.Where(x => x.warehouseID == item.warehouseID && x.goodsID == item.goodsID).OrderByDescending(o => o.operateTime).FirstOrDefault()?.price ?? db.Set<GoodsInfo>().AsNoTracking().Where(x => x.Id == item.goodsID).FirstOrDefault()?.price ?? 0;
                item.amount = item.price * item.quantity;
            }

            var resultAll = inOutWarehousings.Union(posSalesFlows).Union(wholesaleInOuts).Union(checkStocks).Union(otherBills).Union(defectiveBills).ToArray();
            await db.AttachData<GoodsInfo, BusinessFlowsResponse>(resultAll, x => x.goodsID, (m, arr) =>
            {
                m.GoodsInfo = arr.FirstOrDefault();
            });

            //分类赋值
            var categoryData = await db.Set<GoodsCategory>().AsNoTracking().MaxTake().ToArrayAsync();
            //供应商赋值
            var suppliers = await db.Set<Supplier>().AsNoTracking().ToArrayAsync();
            foreach (var m in resultAll)
            {
                m.category = categoryData.FirstOrDefault(x => x.Id == m.GoodsInfo?.categoryID);
                m.rootCategory = FindRoot(categoryData, m.GoodsInfo?.categoryID);
                m.supplierId = queryInOutWarehousing.FirstOrDefault(x => x.warehouseID == m.warehouseID && x.goodsID == m.goodsID)?.supplierID;
            }
            //根据分类条件筛选
            if (!string.IsNullOrWhiteSpace(dto.categoryId))
            {
                resultAll = resultAll.Where(x => x.category?.Id == dto.categoryId).ToArray();
            }

            await db.AttachData<Supplier, BusinessFlowsResponse>(resultAll, x => x.supplierId,
                (m, arr) => m.Supplier = arr.FirstOrDefault());
            //根据供应商条件筛选
            if (!string.IsNullOrWhiteSpace(dto.supplierId))
            {
                resultAll = resultAll.Where(x => x.supplierId == dto.supplierId).ToArray();
            }

            resultAll = resultAll.OrderBy(o => o.warehouseID).ThenByDescending(o => o.GoodsInfo?.No).OrderBy(o => o.operateTime).ToArray();

            var response = new PagedResultDto<BusinessFlowsResponse>();
            response.TotalCount = resultAll.Count();
            int pageSize = dto.PageSize == 0 ? 200 : dto.PageSize, pageIndex = dto.Page == 0 ? 1 : dto.Page;
            //总页数
            var pageCount = (resultAll.Count() + pageSize - 1) / pageSize;
            var result = resultAll.Skip((pageIndex - 1) * pageSize).Take(pageSize).ToList();

            response.Items = result;
            
            //仓库赋值
            await db.AttachData<Warehouse.Entity.Warehouse, BusinessFlowsResponse>(result, x => x.warehouseID,
                (m, arr) => m.Warehouse = arr.FirstOrDefault());

            return response;
        }
        #endregion
    }
}
